﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Universe.SimpleQueue
{
    internal class AdoHelper
    {

        private static readonly char[] digits =
        {
            '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f', 'g', 'h',
            'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z',
            '$', '@'
        };

        public static byte[] ReadBinary(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? null : (byte[]) rdr.GetValue(n);
        }

        public static string ReadString(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? null : rdr.GetString(n);
        }

        public static Int32? ReadInt32(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? default(Int32?) : rdr.GetInt32(n);
        }

        public static DateTime ReadDate(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? default(DateTime) : rdr.GetDateTime(n);
        }

        public static DateTime? ReadDateNullable(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? (DateTime?)null : rdr.GetDateTime(n);
        }

        public static bool ReadBoolean(DbDataReader rdr, string fieldName)
        {
            int n = rdr.GetOrdinal(fieldName);
            if (n < 0)
                throw new ArgumentException("Column " + fieldName + " isn't found");

            return rdr.IsDBNull(n) ? default(Boolean) : rdr.GetBoolean(n);
        }

        public static DbCommand BindParameters(DbConnection con, DbTransaction tran, string sql, object pars)
        {
            var ret = con.CreateCommand();
            ret.CommandType = CommandType.Text;
            ret.CommandText = sql;
            ret.Connection = con;
            ret.Transaction = tran;
            foreach (Tuple<string, object> par in AsList(sql, pars))
            {
                var name = par.Item1;
                var value = par.Item2;
                var p = ret.CreateParameter();
                p.ParameterName = "@" + name;
                if (value == null)
                {
                    p.DbType = DbType.String;
                    p.Value = DBNull.Value;
                }

                else if (value is int)
                {
                    p.DbType = DbType.Int32;
                    p.Value = value;
                }
                else if (value is string)
                {
                    p.DbType = DbType.String;
                    p.Value = value;
                }
                else if (value is DateTime)
                {
                    p.DbType = DbType.DateTime;
                    p.Value = value;
                }
                else if (value is byte[])
                {
                    p.DbType = DbType.Binary;
                    p.Value = value;
                    p.Size = ((byte[]) value).Length;
                }
                else
                {
                    p.Value = value;
                }


                ret.Parameters.Add(p);
            }

            return ret;
        }

        private static readonly object[] ZeroArray = new object[0];

        private static readonly Dictionary<string, List<Tuple<string, PropertyInfo>>> Cache =
            new Dictionary<string, List<Tuple<string, PropertyInfo>>>();

        private static readonly object SyncCache = new object();

        private static List<Tuple<string, object>> AsList(string sql, object pars)
        {
            List<Tuple<string, PropertyInfo>> properties;
            lock (SyncCache)
            {
                if (!Cache.TryGetValue(sql, out properties))
                {
                    properties = new List<Tuple<string, PropertyInfo>>();
                    foreach (var p1 in pars.GetType().GetProperties())
                    {
                        var name = p1.Name;
                        properties.Add(new Tuple<string, PropertyInfo>(name, p1));
                    }

                    Cache[sql] = properties;
                }
            }


            List<Tuple<string, object>> ret = new List<Tuple<string, object>>();
            foreach (var p1 in properties)
            {
                var value = p1.Item2.GetValue(pars, ZeroArray);
                var name = p1.Item1;
                ret.Add(new Tuple<string, object>(name, value));
            }

            return ret;
        }
    

        public static string UuidTo26InsensitiveChars(Guid uuid)
        {
            if (uuid == Guid.Empty) return null;

            StringBuilder ret = new StringBuilder(26);
            // String tmp1 = "";
            var allBytes = uuid.ToByteArray();

            long[] longs = new long[] {BitConverter.ToInt64(allBytes, 0), BitConverter.ToInt64(allBytes, 8)};
            int buffer = 0;
            int pos = 0;
            foreach (long l in longs)
            {
                long l2 = l;
                for (int i = 0; i < 64; i++)
                {
                    byte bit = (byte) (int) (l2 & 1);
                    l2 = l2 >> 1;
                    buffer |= (bit << pos);
                    if (++pos == 5)
                    {
                        ret.Append(digits[buffer]);
                        buffer = 0;
                        pos = 0;
                    }
                }
            }

            if (pos != 0)
                ret.Append(digits[buffer]);

            return ret.ToString();

        }
    }
}

